package concurrent.adder;

import com.sun.jmx.snmp.tasks.ThreadService;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author harker777
 */
public class ConcurrentAdderDemo {

	private static final int THREADS_NUMBER = 1000;
	private static final int GENERATOR_CAP = 50;
	private static final int ARRAY_SIZE = 100000;

	/**
	 * @param args the command line arguments
	 */
	public static void main(String[] args) {
		// Generate a random Integer vector and print it
		Integer[] data = generateData(ARRAY_SIZE, GENERATOR_CAP);
		printData(data);

		// Get the boundaries for the threads using TaskDistributor class
		TaskDistributor distributor = new TaskDistributorImpl();
		distributor.distributeTasks(THREADS_NUMBER, ARRAY_SIZE);
		Integer[] froms = distributor.getFroms();
		Integer[] tos = distributor.getTos();

		// Spawn the threads! *read in a voice like "Release the Kraken!"*
		ValueReturningRunnable<Integer>[] adders = new Adder[THREADS_NUMBER];
		Thread[] threads = new Thread[THREADS_NUMBER];
		spawnThreads(adders, threads, data, froms, tos);

		// Now wait for all threads to finish their work
		waitForThreads(threads);

		// Now get the results, sum them and print their sum
		Integer result = sumTheResults(adders);
		System.out.println(result);

		// And get the real value = just for testing purposes
		Integer test = 0;
		for (int i = 0; i < data.length; i++) {
			test += data[i];
		}
		System.out.println(test);
	}

	public static Integer[] generateData(int arraySize, int generatorCap) {
		Random random = new Random();
		Integer[] result = new Integer[arraySize];
		for (int i = 0; i < arraySize; i++) {
			result[i] = random.nextInt(generatorCap);
		}
		return result;
	}

	public static void printData(Integer[] data) {
		for (Integer value : data) {
			System.out.print(value + " ");
		}
		System.out.print("\n");
	}

	private static void spawnThreads(ValueReturningRunnable<Integer>[] adders, Thread[] threads,
									 Integer[] data, Integer[] froms, Integer[] tos) {
		for (int i = 0; i < THREADS_NUMBER; i++) {
			adders[i] = new Adder(data, froms[i], tos[i]);
			threads[i] = new Thread(adders[i]);
			threads[i].start();
		}
	}

	private static void waitForThreads(Thread[] threads) {
		for (int i = 0; i < THREADS_NUMBER; i++) {
			try {
				threads[i].join();
			} catch (InterruptedException ex) {
				System.out.println("Interrupted");
			}
		}
	}

	private static Integer sumTheResults(ValueReturningRunnable<Integer>[] adders) {
		Integer result = 0;
		for (int i = 0; i < THREADS_NUMBER; i++) {
			result += adders[i].getValue();
		}
		return result;
	}
}
